function defineReactive(target) {
  const keys = object.keys(target);
  if (keys instanceof Array) {
    keys.forEach((key) => {
      const item = target[key];
      if (Object.toString.call(item) === "[object Object]") {
        defineReactive(target[key]);
      } else {
        observe(target, key);
      }
    });
  }
  return target;
}

const targetStack = [];

function observe(target, key) {
  const dep = new Dep();
  Object.defineProperty(target, key, {
    set(payload) {
      target[key] = payload;
      dep.notify();
    },
    get() {
      dep.depend();
      return target[key];
    },
  });
}

class Dep {
  constructor() {
    this.deps = new Set();
  }

  depend() {
    if (Dep.target) {
      this.deps.add(Dep.target);
    }
  }

  notify() {
    this.deps.forEach((watcher) => {
      watcher.update();
    });
  }
}

Dep.target = null;

class Watcher {
  constructor(getter, options) {
    this.getter = getter;
    this.value = "";

    const { computed } = options;

    if (computed) {
      this.deps = new Dep();
    } else {
      this.get(); // 初始化的时候，Dep.target
    }
  }

  get() {
    pushTarget(this);
    this.value = this.getter();
    popTarget();
    return this.value;
  }

  depend() {
    if (Dep.target) {
      this.deps.depend();
    }
  }

  update() {
    this.get();
  }
}

function pushTarget(_target) {
  if (Dep.target) {
    targetStack.push(_target);
  }
  Dep.target = _target;
}

function popTarget() {
  Dep.target = targetStack.pop();
}

const data = defineReactive({
  msg: "hello world",
});

new Watcher(() => {
  document.getElementById("app").innerHTML = `${data.msg}`;
});

function computed(getter) {
  const computedWatcher = new Watcher(getter, { computed: true });

  const ref = {};

  Object.defineProperty(ref, "value", {
    get() {
      computedWatcher.depend();
      return computedWatcher.get();
    },
  });
}
